package liziy.spring.context.annotation;

import liziy.spring.core.stereotype.Component;
import liziy.spring.beans.factory.config.AnnotationBeanNameGenerator;
import liziy.spring.beans.factory.config.BeanDefinition;
import liziy.spring.beans.BeanDefinitionRegistry;
import liziy.spring.beans.factory.config.BeanNameGenerator;
import liziy.spring.core.classreading.type.AnnotatedMetadata;
import liziy.spring.core.classreading.type.ScopeMetadata;
import liziy.spring.core.classreading.type.ScopeMetadataResolver;
import liziy.spring.core.classreading.type.SimpleAnnotatedMetadata;


import java.io.File;
import java.net.URL;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;

/**
 * 根据包路径提供的候选处理的BeanDefinition对象并注册到默认容器中
 * （找到符合容器 管理的的对象）
 * @author Liziy
 * @date 2020/12/16 0:35
 **/
public class ClassPathBeanDefinitionScanner {
    private List<Class<?>> annotatedClassse = new ArrayList<>();
    private BeanDefinitionRegistry registry;
    //创建别名生成对象
    private BeanNameGenerator beanNameGenerator = AnnotationBeanNameGenerator.INSTANCE;

    public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry){
        this.registry = registry;
        //注册如何筛选符合条件的bean定义对象
        registerDefaultFilters();
    }

    private void registerDefaultFilters() {
        //添加容器默认解析注解
        this.annotatedClassse.add(Component.class);
    }

    public void scan(String[] basePackages) {
        doScan(basePackages);
    }

    /**
     * 真正执行扫描操作
     * @param basePackages
     */
    private void doScan(String... basePackages) {
        //定义一个集合存储来存贮各个包扫描后得到的BeanDefinition对象
        Set<BeanDefinition> definitions = new LinkedHashSet<>();
        for (String basePackage :basePackages){
            //开始递归查找包路径下面所有.class文件
            Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
            //遍历当前这个包路径扫描所有候选BeanDefinition对象
            for (BeanDefinition candidate : candidates){
                //设置候选bean的作用域 property | singleton
                ScopeMetadata scopeMetadata = new ScopeMetadataResolver((AnnotatedBeanDefintion) candidate);
                candidate.setScope(scopeMetadata.resolverScopeName());
                //设置候选bean的别名[1、注解名称 2、首字母小写]
                String beanName = this.beanNameGenerator.buildBeanNameGenerator(candidate);
                //注册到默认容器的集合中
                registry.registryBeanDefinition(beanName, candidate);
            }
        }
    }

    private Set<BeanDefinition> findCandidateComponents(String basePackage) {
        // 存储筛选后bean定义对象
        Set<BeanDefinition> candidates = new LinkedHashSet<>();
        //1. 获取到了当前包路径下面的所有类对象集合
        Set<Class<?>> classSet = getClassFileByBasePackage(basePackage);
        //2. 判断该类是否是容器需要管理的类
        for (Class<?> cls : classSet) {
            //创建元素处理对象
            AnnotatedMetadata metadata = new SimpleAnnotatedMetadata(cls);
            //判断是否有@Compnonet注解
            if (isCandidateComponent(metadata)) {
                //判断是否是一个接口/抽象类
                if (metadata.isConcrete()) {
                    //需要把符合条件的类对象封装为一个BeanDefinition对象
                    //作用域,beanClass,beanName;
                    ScannedAnnotationBeanDefinition scannedAnnotationBeanDefinition
                            = new ScannedAnnotationBeanDefinition(metadata);
                    candidates.add(scannedAnnotationBeanDefinition);
                }
            }
            //判断有@Compnonet注解上的类是否可以进行实例化(进行处理的过程中包括接口/抽象类)
            //以上2个条件都满足,就会放置到扫描候选beanDefinition集合中
        }
        return candidates;
    }

    /**
     * 判断是否包含@Component注解
     * @param metadata
     * @return
     */
    private boolean isCandidateComponent(AnnotatedMetadata metadata) {
        //判断当前处理类是否是过滤器中规定数据
        for (Class<?> annotatedClass : annotatedClassse) {
            if (metadata.getAnnotationTypes().contains(annotatedClass)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 递归获取指定的包路径下面的所有类文件
     * @param basePackage
     * @return
     */
    public Set<Class<?>> getClassFileByBasePackage(String basePackage){
        Set<Class<?>> classSet = new LinkedHashSet<>();
        //1.根据项目根路径获取项目编译目录
        URL url = this.getClass().getClassLoader().getResource(basePackage.replaceAll("\\.","/"));
        //2.根据url构建一个文件对象
        File dir = new File(url.getFile());
        try {
            for (File file : dir.listFiles()) {
                //3.递归获取当前文件下所有.class文件
                if (file.isDirectory()) {
                    //递归找下面的文件
                    Set<Class<?>> classFileByUrl = getClassFileByBasePackage(basePackage + "." + file.getName());
                    classSet.addAll(classFileByUrl);
                }else {
                    String fileName = file.getName();
                    if (fileName.endsWith(".class")) {
                        //组装类路径
                        String classPathString = basePackage + "." + fileName;
                        classPathString = classPathString.replaceAll("\\.class", "");
                        classSet.add(Class.forName(classPathString));
                    }
                }
            }
        }catch (Exception e){
            e.printStackTrace();
        }

        return classSet;

    }

    public static void main(String[] args) {
        Set<Class<?>> classSet = new ClassPathBeanDefinitionScanner(null)
                .getClassFileByBasePackage("cn.liziy");
        System.out.println(classSet);
        System.out.println("==============");
        Set<BeanDefinition> candidateComponents
                = new ClassPathBeanDefinitionScanner(null)
                    .findCandidateComponents("cn.liziy");
        candidateComponents.forEach(e-> System.out.println(e.getBeanClass()));
    }
}
